import { computed, reactive, watch } from "vue";
import { Grid, Minimap } from "@/g6/g6";
import { flatten, suffixSize } from "@/utils/utils";
import { Command, useCommander } from "@/plugins/Command";
import { getSelected, SELECTED_STATE } from "@/behavior/click-select";
import clone from "@antv/util/lib/clone";

export const VueFlowEditorProvider = "@@VueFlowEditorProvider";

/**
 * 编辑器各个节点整体样式
 * @author  韦胜健
 * @date    2020/4/29 15:32
 */
export function useEditorStyles(props: { height; menuWidth; toolbarHeight }) {
  return computed(() => ({
    root: {
      height: suffixSize(props.height),
    },
    left: {
      width: suffixSize(props.menuWidth),
    },
    toolbar: {
      height: suffixSize(props.toolbarHeight),
    },
  }));
}

/**
 * 配置G6的插件信息，并且根据props中的属性动态 加载/卸载 插件
 * @author  韦胜健
 * @date    2020/4/29 11:44
 */
export function useEditorPlugins(props: { grid; miniMap }, graph) {
  const pluginState = reactive({
    plugin: {
      grid: null, // 当前网格插件加载的配置信息对象
      miniMap: null, // 当前缩略图插件加载的配置信息对象
    },
  });

  function grid(val) {
    if (val === false || val == null) {
      if (!!pluginState.plugin.grid) {
        graph.removePlugin(pluginState.plugin.grid);
        pluginState.plugin.grid = null;
      }
    } else {
      const pluginConfig = typeof val === "boolean" ? {} : val;
      pluginState.plugin.grid = new Grid(pluginConfig);
      graph.addPlugin(pluginState.plugin.grid);
    }
  }

  function miniMap(val) {
    if (val === false || val == null) {
      if (!!pluginState.plugin.miniMap) {
        graph.removePlugin(pluginState.plugin.miniMap);
        pluginState.plugin.miniMap = null;
      }
    } else {
      const pluginConfig = typeof val === "boolean" ? {} : val;
      pluginState.plugin.miniMap = new Minimap(pluginConfig);
      graph.addPlugin(pluginState.plugin.miniMap);
    }
  }

  grid(props.grid);
  miniMap(props.miniMap);

  watch(
    () => props.grid,
    (val) => {
      grid(val);
    }
  );

  watch(
    () => props.miniMap,
    (val) => {
      miniMap(val);
    }
  );

  return {
    pluginState,
  };
}

/**
 * 对canvas组件所需要的属性做响应式缓存
 * @author  韦胜健
 * @date    2020/4/29 16:46
 */
export function useCanvasProps(props: { data?; grid; miniMap }) {
  const canvasProps = reactive({
    data: props.data,
    grid: props.grid,
    miniMap: props.miniMap,
  });

  watch(
    () => props.data,
    (val) => (canvasProps.data = val)
  );
  watch(
    () => props.grid,
    (val) => (canvasProps.grid = val)
  );
  watch(
    () => props.miniMap,
    (val) => (canvasProps.miniMap = val)
  );

  return canvasProps;
}

/**
 * 注册编辑器常用的命令，以便撤销以及重做命令
 * @author  韦胜健
 * @date    2020/4/30 15:19
 */
export function useEditorCommander(editorState) {
  const commander = useCommander(editorState);

  /*网格切换*/
  commander.register(
    new Command({
      name: "switchGrid",
      execute() {
        const grid = editorState.canvasProps.grid;
        return {
          redo: () => {
            editorState.canvasProps.grid = !grid;
          },
          undo: () => {
            editorState.canvasProps.grid = grid;
          },
        };
      },
    })
  );

  /*缩略图切换*/
  commander.register(
    new Command({
      name: "switchMiniMap",
      execute() {
        const miniMap = editorState.canvasProps.miniMap;
        return {
          redo: () => {
            editorState.canvasProps.miniMap = !miniMap;
          },
          undo: () => {
            editorState.canvasProps.miniMap = miniMap;
          },
        };
      },
    })
  );

  /*适应画布切换*/
  commander.register(
    new Command({
      name: "fitView",
      execute() {
        const graph = this.graph;
        const zoom = graph.getZoom();

        return {
          redo: () => {
            graph.fitView(20);
          },
          undo: () => {
            graph.zoomTo(zoom);
          },
        };
      },
    })
  );

  /*实际尺寸切换*/
  commander.register(
    new Command({
      name: "actualView",
      execute() {
        const graph = this.graph;
        const zoom = graph.getZoom();
        return {
          redo: () => {
            graph.zoomTo(1);
          },
          undo: () => {
            graph.zoomTo(zoom);
          },
        };
      },
    })
  );

  /*放大*/
  commander.register(
    new Command({
      name: "zoomIn",
      keyboard: "ctrl+=",
      execute() {
        const graph = this.graph;
        let zoom = graph.getZoom();

        return {
          redo: () => {
            graph.zoom(1.1);
          },
          undo: () => {
            graph.zoomTo(zoom);
          },
        };
      },
    })
  );

  /*缩小*/
  commander.register(
    new Command({
      name: "zoomOut",
      keyboard: "ctrl+-",
      execute() {
        const graph = this.graph;
        let zoom = graph.getZoom();

        return {
          redo: () => {
            graph.zoom(0.9);
          },
          undo: () => {
            graph.zoomTo(zoom);
          },
        };
      },
    })
  );

  /*删除节点*/
  commander.register(
    new Command({
      name: "delete",
      keyboard: ["ctrl+d", "backspace", "delete"],
      init() {
        this.data = {
          onSelectChange: (selected) => {
            this.data = {
              ...this.data,
              selected,
            };
          },
        };
        this.graph.on("select-change", this.data.onSelectChange);
      },
      destroy() {
        this.graph.off("select-change", this.data.onSelectChange);
      },
      execute() {
        const graph = this.graph;

        let before;
        let after;

        return {
          redo: async () => {
            if (!before && !after) {
              const { beforeDelete, afterDelete } = editorState.props;

              before = clone(graph.save());
              await Promise.all(
                flatten(Object.values(getSelected(graph))).map(async (i) => {
                  const model = i.get("model");
                  const type = i.get("type");
                  try {
                    if (!!beforeDelete) {
                      await beforeDelete(model, type);
                    }
                    i.clearAnchor();
                    graph.removeItem(i);
                    graph.paint();
                    if (!!afterDelete) {
                      await afterDelete(model, type);
                    }
                  } catch (e) {
                    console.error(e);
                  }
                })
              );
              after = clone(graph.save());
            } else {
              graph.read(after);
            }
          },
          undo: () => {
            graph.read(before);
          },
        };
      },
      isEnable() {
        let { selected } = this.data;
        if (!selected) {
          return false;
        } else {
          const { nodes, edges } = selected;
          return [...nodes, ...edges].length > 0;
        }
      },
    })
  );

  /*拖拽节点*/
  commander.register(
    new Command({
      name: "drag",
      doNothingWhenExecute: true,
      init() {
        this.data = {
          ondragstart: () => {
            this.data = {
              ...this.data,
              start: clone(this.graph.save()),
            };
          },
          ondragend: () => {
            this.data = {
              ...this.data,
              end: clone(this.graph.save()),
            };

            commander.commands.drag();
          },
        };
        this.graph.on("drag-node:start", this.data.ondragstart);
        this.graph.on("drag-node:end", this.data.ondragend);
      },
      destroy() {
        this.graph.off("drag-node:start", this.data.ondragstart);
        this.graph.off("drag-node:end", this.data.ondragend);
      },
      execute() {
        const { start, end } = this.data;
        return {
          redo: () => {
            this.graph.read(end);
          },
          undo: () => {
            this.graph.read(start);
          },
        };
      },
    })
  );

  /*全选*/
  commander.register(
    new Command({
      name: "selectAll",
      keyboard: "ctrl+a",
      isQueue: false,
      execute() {
        return {
          redo: () => {
            if (editorState.props.multipleSelect) {
              const selected = {
                nodes: this.graph.findAll("node", () => true),
                edges: this.graph.findAll("edge", () => true),
              };
              flatten(Object.values(selected)).forEach((item) => {
                item.setState(SELECTED_STATE, true);
              });
              this.graph.paint();
              this.graph.emit("select-change", selected);
            }
          },
        };
      },
    })
  );

  /*添加连接线*/
  commander.register(
    new Command({
      name: "addEdge",
      doNothingWhenExecute: true,
      init() {
        this.data = {
          beforeAdd: () => {
            this.data = {
              ...this.data,
              before: clone(this.graph.save()),
            };
          },
          afterAdd: () => {
            this.data = {
              ...this.data,
              after: clone(this.graph.save()),
            };
            commander.commands.addEdge();
          },
        };
        this.graph.on("add-edge:before", this.data.beforeAdd);
        this.graph.on("add-edge:after", this.data.afterAdd);
      },
      destroy() {
        this.graph.off("add-edge:before", this.data.beforeAdd);
        this.graph.off("add-edge:after", this.data.afterAdd);
      },
      execute() {
        const { before, after } = this.data;
        return {
          redo: () => {
            this.graph.read(after);
          },
          undo: () => {
            this.graph.read(before);
          },
        };
      },
    })
  );

  /**
   * 添加节点
   * @author  韦胜健
   * @date    2020/5/9 9:25
   */
  commander.register(
    new Command({
      name: "addNode",
      doNothingWhenExecute: true,
      init() {
        this.data = {
          beforeAdd: () => {
            this.data = {
              ...this.data,
              before: clone(this.graph.save()),
            };
          },
          afterAdd: () => {
            this.data = {
              ...this.data,
              after: clone(this.graph.save()),
            };
            commander.commands.addNode();
          },
        };
        this.graph.on("add-node:before", this.data.beforeAdd);
        this.graph.on("add-node:after", this.data.afterAdd);
      },
      destroy() {
        this.graph.off("add-node:before", this.data.beforeAdd);
        this.graph.off("add-node:after", this.data.afterAdd);
      },
      execute() {
        const { before, after } = this.data;
        return {
          redo: () => {
            this.graph.read(after);
          },
          undo: () => {
            this.graph.read(before);
          },
        };
      },
    })
  );

  /**
   * 更新节点
   * @author  韦胜健
   * @date    2020/5/9 9:25
   */
  commander.register(
    new Command({
      name: "update",
      doNothingWhenExecute: true,
      init() {
        this.data = {
          before: null,
          after: null,
        };
      },
      execute(model) {
        const graph = this.graph;
        const item = graph.findById(model.id);

        this.data.before = clone(graph.save());
        if (!!item) {
          graph.update(item, model);
          graph.paint();
          this.data.after = clone(graph.save());
        } else {
          this.data.after = this.data.before;
        }

        const { before, after } = this.data;

        return {
          redo: () => {
            graph.read(after);
          },
          undo: () => {
            graph.read(before);
          },
        };
      },
    })
  );

  return commander;
}
